package com.pwc.conversionTool.biz.sendSoap;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.xml.soap.SOAPException;

import org.dom4j.DocumentException;

import com.google.appengine.api.blobstore.BlobKey;
import com.googlecode.objectify.Key;
import com.pwc.conversionTool.XML.XMLBase;
import com.pwc.conversionTool.biz.ProjectBiz;
import com.pwc.conversionTool.biz.TaskBiz;
import com.pwc.conversionTool.dao.DataAdaptorDao;
import com.pwc.conversionTool.dao.LogDao;
import com.pwc.conversionTool.dao.TaskDao;
import com.pwc.conversionTool.dataAdaptor.CsvUtil;
import com.pwc.conversionTool.dataAdaptor.DataAdaptor;
import com.pwc.conversionTool.dataAdaptor.IDataAdaptor;
import com.pwc.conversionTool.entity.Account;
import com.pwc.conversionTool.entity.CsvDataType;
import com.pwc.conversionTool.entity.DataTemplate;
import com.pwc.conversionTool.entity.DataType;
import com.pwc.conversionTool.entity.TaskFlowLog;
import com.pwc.conversionTool.entity.Operation;
import com.pwc.conversionTool.entity.SoapConnection;
import com.pwc.conversionTool.entity.Task;
import com.pwc.conversionTool.entity.TaskFlow;
import com.pwc.conversionTool.entity.TaskFlowRunningType;
import com.pwc.conversionTool.entity.TaskLog;
import com.pwc.soapUtil.SoapUtil;

import static com.pwc.GAE.blobstore.BlobstoreUtil.*;

public class SendProcess {

	private Account account;

	private TaskFlow taskFlow;

	private List<Task> tasks;

	private TaskFlowLog log;

	private TaskBiz taskBiz = new TaskBiz();
	private TaskDao taskDao=new TaskDao();
	private ProjectBiz projectBiz = new ProjectBiz();
	private LogDao logDao = new LogDao();
	private DataAdaptorDao dataAdaptorDao = new DataAdaptorDao();
	private SoapUtil soapUtil = new SoapUtil();
	
	private CsvProcess csvProcess;

	public SendProcess(Account account, TaskFlow taskFlow) {
		super();
		this.account = account;
		this.taskFlow = taskFlow;
		this.tasks = taskBiz.getTasksByTaskFlowId(taskFlow.getId());
		this.log = new TaskFlowLog(taskFlow.getTaskFlowName(),taskFlow.getKey());
	}

	
	
	public boolean runWorkFlow() {
		//change status
		taskFlow.setStatus(TaskFlowRunningType.RUNNING);
		
		taskDao.saveTaskFlow(taskFlow);
		
		try {

			for (Task task : tasks) {
				log.addMessage(task.getTaskName()+" is start to run.");
				boolean result= task(task);
				
				if(!result)
				{
					//error handle
					
					log.setHasError(true);
				}
				
				log.addMessage(task.getTaskName()+" is completed.");
			}

		} catch (Exception e) {
			log.addMessage("error in run workflow");
			log.addMessage(e.getMessage());
			log.addMessage(Arrays.toString(e.getStackTrace()));
			e.printStackTrace();

		} finally {
			taskFlow.setStatus(TaskFlowRunningType.ERROR);
			logDao.saveLog(log);
		}
		
		if(log.isHasError())
		{
			taskFlow.setStatus(TaskFlowRunningType.ERROR);
		}else {
			taskFlow.setStatus(TaskFlowRunningType.COMPLETE);
		}
		
		
		taskDao.saveTaskFlow(taskFlow);
		
		return !log.isHasError();
	}
	
	
	

	/**
	 * run task 
	 * @param task
	 * @throws IOException
	 * @throws UnsupportedOperationException
	 * @throws SOAPException
	 * @throws DocumentException
	 */
	public boolean task(Task task) throws IOException, UnsupportedOperationException, SOAPException, DocumentException {
		
		TaskLog taskLog=new TaskLog(task.getTaskName(),task.getKey());
		taskLog.addMessage(task.getTaskName() +" begin to run! ");
		
		try {

			List<String> keys = task.getFiles();

			Key<Operation> operationkey = task.getOperation();

			Operation operation = projectBiz.getOperationByKey(operationkey);

			List<IDataAdaptor> dataAdaptors = new ArrayList<IDataAdaptor>();

			if (operation.getDataAdaptorData().getDataType().equalsIgnoreCase(DataType.CSV)) {
				csvProcess=new CsvProcess(taskLog,operation, keys);
				dataAdaptors = csvProcess.getDataAdaptors();

				if (dataAdaptors != null) {
					
					for (IDataAdaptor dataAdaptor : dataAdaptors) {
						List<String> payloads=generatePayload(operation,dataAdaptor,taskLog);
						for (String payload : payloads) {
							String result= sendSoap(operation,payload);
							/// handle error result
							//or record result
							taskLog.addMessage(result);
						}
					}
				}
			}
		} catch (Exception e) {
			taskLog.addMessage("task running has error!");
			taskLog.addMessage(e.getMessage());
			taskLog.setHasError(true);
			logDao.saveLog(taskLog);
			return false;
		}
		
		taskLog.addMessage("task running complete!");
		logDao.saveLog(taskLog);
		return true;
	}
	
	

	/**
	 * get payload list 
	 * @param operation
	 * @param dataAdaptor
	 * @return
	 * @throws DocumentException
	 */
	public List<String> generatePayload(Operation operation, IDataAdaptor dataAdaptor,TaskLog tasklog) throws DocumentException {
		tasklog.addMessage("begin to generate payload");
		
		List<String> payloads = new ArrayList<String>();
		String template = operation.getSoapTemplate();
		Map<String, String> mapping = operation.getMapping();

		List<String[]> content = dataAdaptor.getContent();

		for (String[] row : content) {
			XMLBase xmlUtil = new XMLBase();
			xmlUtil.readStringToXML(template);

			for (Map.Entry<String, String> entry : mapping.entrySet()) {
				
				String[] value = entry.getValue().split("|");
				if (value.length == 2 && "header".equals(value[0])) {
					int counter = -1;
					for (String header : dataAdaptor.getHeader()) {
						
						if(header.equalsIgnoreCase(value[1]))
						{
							counter++;
							break;
						}
						
					}
					if(counter==-1)
					{
						tasklog.setHasError(true);
						tasklog.addMessage("has error in mapping header");
						return null;
					}else {
						xmlUtil.setSingleStringValue(entry.getKey(), row[counter]);						
					}
					
				}
			}
			tasklog.addMessage("generate payload success...");
			payloads.add(xmlUtil.toString());
		}

		return payloads;
	}

	
	/**
	 * sending soap
	 * @param operation
	 * @param payload
	 * @return
	 * @throws UnsupportedOperationException
	 * @throws SOAPException
	 * @throws IOException
	 */
	public String sendSoap(Operation operation, String payload) throws UnsupportedOperationException, SOAPException, IOException {
		SoapConnection connection = operation.getSoapConnectionData();

		String result = soapUtil.SendSoapBase(payload, connection.getEndpoint(), connection.getUserName(), connection.getPassword(),
				connection.getPasswordType());

		return result;
	}

	
	


}
